package com.yehun.config;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.JedisCommands;
import redis.clients.jedis.ShardedJedisPool;

public class RedisLock {

    public final static Logger logger = LoggerFactory.getLogger(RedisLock.class);

    private JedisCommands jedisCommands;
    public void setJedisCommands(JedisCommands jedisCommands) {
        this.jedisCommands = jedisCommands;
    }

    private String lockKey;
    public void setLockKey(String lockKey) {
        this.lockKey = lockKey;
    }
    private String lockValue;
    private long timeout;
    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }

    private static final long DEFAULT_TIMEOUT = 2000L; //默认超时时间，单位: 毫秒
    private static final long DEFAULT_WAIT_TIME = 2L; //默认等待时间，单位: 毫秒

    public static RedisLock getInstance() {
        return new RedisLock();
    }

    public RedisLock() { }

    public RedisLock(String lockKey) {
        this(lockKey, DEFAULT_TIMEOUT);
    }

    public RedisLock(String lockKey, long timeout) {
        this.lockKey = lockKey;
        this.timeout = timeout;
    }

    public void lock() {
        initJedis();
        int count = 0;
        for (;;) {
            lockValue = getTime() + "";
            Long result = jedisCommands.setnx(lockKey, lockValue);
            if(result == 1){
                break;
            }
            if(isTimeout() && count++ % 20 == 0){
                jedisCommands.del(lockKey);
            }
            try {
                Thread.sleep(DEFAULT_WAIT_TIME);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean tryLock() {
        initJedis();
        lockValue = getTime() + "";
        Long result = this.jedisCommands.setnx(lockKey, lockValue);
        if (result == 1){
            return true;
        }
        if (isTimeout()){
            this.jedisCommands.del(lockKey);
        }
        return false;
    }

    public boolean tryLock(int timeout) {
        initJedis();
        boolean flag = false;
        Long time = System.currentTimeMillis();
        int count = 0;
        for(;;){
            lockValue = getTime() + "";
            Long result = this.jedisCommands.setnx(lockKey, lockValue);
            if(result == 1){
                flag = true;
                break;
            }
            if(isTimeout() && count++ % 20 == 0){
                this.jedisCommands.del(lockKey);
            }
            try {
                Thread.sleep(DEFAULT_WAIT_TIME);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Long now = System.currentTimeMillis();
            if((now - time) >= timeout){
                break;
            }
        }
        return flag;
    }

    public void unlock() {
        initJedis();
        String oldValue = this.jedisCommands.get(lockKey);
        if(lockValue.equals(oldValue)){
            this.jedisCommands.del(lockKey);
        }
    }

    private boolean isTimeout(){
        initJedis();
        String oldValue = this.jedisCommands.get(lockKey);
        if(oldValue == null){
            return false;
        }
        long now = System.currentTimeMillis();
        long useTime = now - Long.parseLong(oldValue);
        return useTime >= timeout;
    }

    /**
     * 获取当前时间戳，如果无法做到所有的机器时间同步，
     * 可以采用一个机器提供时间服务，这里调用一个时间服务保证所有机器时钟同步
     * @return 时间戳
     */
    private long getTime(){
        return System.currentTimeMillis();
    }

    private void initJedis() {
        if (jedisCommands == null) {
            ShardedJedisPool jedisPool = RedisDataSource.getShardedJedisPool();
            jedisCommands = jedisPool.getResource();
        }
    }
}